/*-------------------------------------------------------------------------
 *
 * printsimple.c
 *	  仅包含有限范围内内置类型的元组的打印例程，无需目录访问。
 *	  这旨在为没有目录访问的后端服务，因为它们不绑定
 *	  到特定数据库，例如一些 walsender 进程。
 *	  它不处理独立后端或除 3.0 之外的协议版本，因为
 *	  当前应用不需要这种处理。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/access/common/printsimple.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/printsimple.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"

/*
 * 启动时，发送 RowDescription 消息。
 */
void printsimple_startup(DestReceiver *fc_self, int fc_operation, TupleDesc fc_tupdesc)
{
	StringInfoData fc_buf;
	int			fc_i;

	pq_beginmessage(&fc_buf, 'T'); /* RowDescription */
	pq_sendint16(&fc_buf, fc_tupdesc->natts);

	for (fc_i = 0; fc_i < fc_tupdesc->natts; ++fc_i)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_i);

		pq_sendstring(&fc_buf, NameStr(fc_attr->attname));
		pq_sendint32(&fc_buf, 0);	/* 表 oid */
		pq_sendint16(&fc_buf, 0);	/* attnum */
		pq_sendint32(&fc_buf, (int) fc_attr->atttypid);
		pq_sendint16(&fc_buf, fc_attr->attlen);
		pq_sendint32(&fc_buf, fc_attr->atttypmod);
		pq_sendint16(&fc_buf, 0);	/* 格式代码 */
	}

	pq_endmessage(&fc_buf);
}

/*
 * 对于每个元组，发送 DataRow 消息。
 */
bool printsimple(TupleTableSlot *fc_slot, DestReceiver *fc_self)
{
	TupleDesc	fc_tupdesc = fc_slot->tts_tupleDescriptor;
	StringInfoData fc_buf;
	int			fc_i;

	/* 确保元组已完全解构 */
	slot_getallattrs(fc_slot);

	/* 准备并发送消息 */
	pq_beginmessage(&fc_buf, 'D');
	pq_sendint16(&fc_buf, fc_tupdesc->natts);

	for (fc_i = 0; fc_i < fc_tupdesc->natts; ++fc_i)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_i);
		Datum		fc_value;

		if (fc_slot->tts_isnull[fc_i])
		{
			pq_sendint32(&fc_buf, -1);
			continue;
		}

		fc_value = fc_slot->tts_values[fc_i];

		/*
		 * 我们不能在这里调用常规类型输出函数，因为我们
		 * 可能没有目录访问权限。因此，我们必须硬编码
		 * 所需类型的知识。
		 */
		switch (fc_attr->atttypid)
		{
			case TEXTOID:
				{
					text	   *fc_t = DatumGetTextPP(fc_value);

					pq_sendcountedtext(&fc_buf,
									   VARDATA_ANY(fc_t),
									   VARSIZE_ANY_EXHDR(fc_t),
									   false);
				}
				break;

			case INT4OID:
				{
					int32		fc_num = DatumGetInt32(fc_value);
					char		fc_str[12];	/* 符号，10 位数字和 '\0' */
					int			fc_len;

					fc_len = pg_ltoa(fc_num, fc_str);
					pq_sendcountedtext(&fc_buf, fc_str, fc_len, false);
				}
				break;

			case INT8OID:
				{
					int64		fc_num = DatumGetInt64(fc_value);
					char		fc_str[MAXINT8LEN + 1];
					int			fc_len;

					fc_len = pg_lltoa(fc_num, fc_str);
					pq_sendcountedtext(&fc_buf, fc_str, fc_len, false);
				}
				break;

			default:
				elog(ERROR, "unsupported type OID: %u", fc_attr->atttypid);
		}
	}

	pq_endmessage(&fc_buf);

	return true;
}
